using System;
using System.Collections.Generic;
using System.Text;
using SharpNeatLib.NeuralNetwork;
using SharpNeatLib.Experiments;

namespace ParaSharpNeatLib
{
    /// <summary>
    /// This is a base class which network evaluators should inherit if they want to be
    /// able to work in a distributed environment.
    /// </summary>
    public abstract class RemoteNetworkEvaluator : MarshalByRefObject, IRemoteNetworkEvaluator
    {
        // Used by GUI to listen for remote evaluation requests
        public delegate void EvaluatingNetworksEventHandler(object sender, EvaluatingNetworksEventArgs e);
        public event EvaluatingNetworksEventHandler EvaluatingNetworksEvent;
        public delegate void EvaluatedNetworkEventHandler(object sender, EvaluatedNetworkEventArgs e);
        public event EvaluatedNetworkEventHandler EvaluatedNetworkEvent;

        #region INetworkEvaluator Members
        public abstract double EvaluateNetwork(INetwork network);
        public abstract string EvaluatorStateMessage {get;}
        #endregion

        #region IRemoteNetworkEvaluator Members
        public abstract void Initialize();        
        public abstract void Dispose();
        public abstract string Name { get;}
        public abstract string Description { get;}
        public abstract string Author { get;}
        public abstract string Version { get;}

        protected List<INetwork> networks;
        protected bool coevolution = false;

        /// <summary>
        /// True if this evaluator is used for coevolution and thus needs to receive
        /// the entire population to perform an evaluation on a network.
        /// </summary>
        public bool Coevolution
        {
            get { return coevolution; }
            set { coevolution = value; }
        }


        /// <summary>
        /// For use by coevolution network evaluators.
        /// 
        /// Networks will be set to the current population before EvaluateNetwork is
        /// called.
        /// </summary>
        public List<INetwork> Networks
        {
            get { return networks; }
            set { networks = value; }
        }

        /// <summary>
        /// Evaluates a list of networks.  This is used to lower communication time and increase
        /// performance, since latency lags bandwidth.
        /// </summary>
        /// <param name="networks">The batch of networks to evaluate.</param>
        /// <returns>The results of each evaluation</returns>
        public double[] EvaluateNetworks(List<INetwork> networks)
        {
            // Inform the GUI that we've begun evaluating a batch of networks
            if (EvaluatingNetworksEvent != null)
                EvaluatingNetworksEvent(this, new EvaluatingNetworksEventArgs(networks));

            double[] results = new double[networks.Count];
            for (int i = 0; i < networks.Count; i++)
            {
                if (networks[i] != null)
                {
                    results[i] = EvaluateNetwork(networks[i]);

                    // Inform the GUI that we've finished evaluating a network
                    if (EvaluatedNetworkEvent != null)
                        EvaluatedNetworkEvent(this, new EvaluatedNetworkEventArgs(networks[i], results[i]));
                }
            }
            return results;
        }
        #endregion
        
        public override Object InitializeLifetimeService()
        {
            // Allow this object to live "forever"
            return null;
        }
    }
}
